package mskruch.gps.data;

import generated.gpx.GpxType;
import generated.gpx.TrackPointExtensionT;
import generated.gpx.TrkType;
import generated.gpx.TrksegType;
import generated.gpx.WptType;
import gpstools.data.UnsupportedFormatException;

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import mskruch.gps.Lap;
import mskruch.gps.Point;
import mskruch.gps.PointFactory;
import mskruch.gps.Track;

/**
 * GPX Content Loader
 * 
 * @author Marcin Skruch
 */
@Deprecated
public class GpxLoader implements TrackLoaderAPI {
    private List<Track> tracks = new ArrayList<Track>();
    private int index = 0;

    public GpxLoader(byte[] data) throws UnsupportedFormatException {
        this(new ByteArrayInputStream(data));
    }

    public GpxLoader(String fileName) throws FileNotFoundException, UnsupportedFormatException {
        this(new FileInputStream(fileName));
    }

    public GpxLoader(InputStream inputStream) throws UnsupportedFormatException {
        try {
            final JAXBElement<GpxType> root = unmarshal(inputStream);
            final GpxType gpx = root.getValue();
            final List<TrkType> gpxTracks = gpx.getTrk();

            for (TrkType gpxTrack : gpxTracks) {
                List<Lap> laps = new ArrayList<Lap>();
                List<TrksegType> gpxLaps = gpxTrack.getTrkseg();
                for (TrksegType gpxLap : gpxLaps) {
                    /* skip empty laps */
                    List<WptType> gpxPoints = gpxLap.getTrkpt();
                    if (!gpxPoints.isEmpty()) {
                        Point[] points = new Point[gpxPoints.size()];
                        int index = 0;
                        for (WptType gpxPoint : gpxPoints) {
                            points[index++] = processPoint(gpxPoint);
                        }
                        laps.add(new Lap(points));
                    }
                }
                if (!laps.isEmpty()) {
                    tracks.add(new Track(gpxTrack.getName(), laps.toArray(new Lap[laps.size()])));
                }
            }
        } catch (JAXBException e) {
            throw new UnsupportedFormatException(e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see itcrowd.gps.data.TrackLoaderAPI#getTracks()
     */
    public Track[] getTracks() {
        return tracks.toArray(new Track[tracks.size()]);
    }

    /*
     * (non-Javadoc)
     * 
     * @see itcrowd.gps.data.TrackLoaderAPI#nextTrack()
     */
    public Track nextTrack() {
        if (index < tracks.size()) {
            return tracks.get(index++);
        } else {
            return null;
        }
    }

    private static JAXBElement<GpxType> unmarshal(InputStream input) throws JAXBException {
        JAXBContext context = JAXBContext.newInstance(GpxType.class, TrackPointExtensionT.class);
        Unmarshaller unmarshaller = context.createUnmarshaller();
        @SuppressWarnings("unchecked")
        JAXBElement<GpxType> root = (JAXBElement<GpxType>) unmarshaller.unmarshal(input);
        return root;
    }

    private static Point processPoint(WptType gpxPoint) {
        /* standard */
        final double lon = gpxPoint.getLon().doubleValue();
        final double lat = gpxPoint.getLat().doubleValue();
        final double ele = gpxPoint.getEle().doubleValue();
        final long time;
        if (gpxPoint.getTime() != null)
            time = gpxPoint.getTime().toGregorianCalendar().getTimeInMillis();
        else
            time = 0;

        /* extensions */
        Short hr = null;
        Short cad = null;

        if (gpxPoint.getExtensions() != null) {
            final List<Object> extensions = gpxPoint.getExtensions().getAny();
            for (Object extension : extensions) {
                if (extension instanceof JAXBElement<?>) {
                    JAXBElement<?> element = (JAXBElement<?>) extension;
                    if (element.getValue() instanceof TrackPointExtensionT) {
                        /* garmin track point v1 extension */
                        TrackPointExtensionT ext = (TrackPointExtensionT) element.getValue();
                        if (ext.getHr() != null) {
                            hr = ext.getHr();
                        }
                        if (ext.getCad() != null) {
                            cad = ext.getCad();
                        }
                    }
                }
            }
        }

        return PointFactory.newPoint(lon, lat, ele, time, hr, cad);
    }

}
